package com.liuh.okhttp;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

import com.liuh.okhttp.builder.GetBuilder;
import com.liuh.okhttp.builder.PostFileBuilder;
import com.liuh.okhttp.builder.PostFormBuilder;
import com.liuh.okhttp.builder.PostStringBuilder;
import com.liuh.okhttp.callback.ICall;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

/**
 * @note okhttp kit
 * @author liuh
 * */
public class OkHttpKit {

	private volatile static OkHttpKit mInstance;
	private OkHttpClient mOkHttpClient;
	//
	public static final long DEFAULT_MILLISECONDS = 10000L;
	private long readTimeOut;
	private long writeTimeOut;
	private long connTimeOut;

	private OkHttpKit(OkHttpClient okHttpClient) {
		if (okHttpClient == null) {
			mOkHttpClient = new OkHttpClient();
		} else {
			mOkHttpClient = okHttpClient;
		}
	}

	public static OkHttpKit initClient(OkHttpClient okHttpClient) {
		if (mInstance == null) {
			synchronized (OkHttpKit.class) {
				if (mInstance == null) {
					mInstance = new OkHttpKit(okHttpClient);
				}
			}
		}
		return mInstance;
	}

	public static OkHttpKit getInstance() {
		return initClient(null);
	}

	public OkHttpClient getOkHttpClient() {
		return mOkHttpClient;
	}

	public OkHttpKit readTimeOut(long readTimeOut) {
		this.readTimeOut = readTimeOut;
		return this;
	}

	public OkHttpKit writeTimeOut(long writeTimeOut) {
		this.writeTimeOut = writeTimeOut;
		return this;
	}

	public OkHttpKit connTimeOut(long connTimeOut) {
		this.connTimeOut = connTimeOut;
		return this;
	}

	/**
	 * @note 异步请求
	 * @author liuh
	 */
	public void execute(Request request, Callback responseCallback) {
		buildCall(request).enqueue(responseCallback);
	}
	
	/**
	 * @note 异步请求(扩展回调接口)
	 * @author liuh
	 * */
	public void execute(Request request, ICall<?> callB) {
		Call call = buildCall(request);
		if (callB == null) {
			callB = ICall.CALLBACK_DEFAULT;
		}
		final ICall<?> finalCallB = callB;
		callB.onBefore(request);
		call.enqueue(new Callback() {

			@Override
			public void onFailure(Call arg0, IOException arg1) {
				failCallBack(arg0, arg1, finalCallB);
			}

			@Override
			public void onResponse(Call arg0, Response arg1) throws IOException {
				try {
					if (!arg1.isSuccessful()) {
						failCallBack(arg0, new IOException("request failed , reponse's code is : " + arg1.code()),
								finalCallB);
						return;
					}

					Object obj = finalCallB.parseNetworkResponse(arg1);
					successCallBack(obj, finalCallB);
				} catch (Exception e) {
					failCallBack(arg0, e, finalCallB);
				} finally {
					if (arg1.body() != null)
						arg1.body().close();
				}
			}

		});
	}
	
	private void failCallBack(Call call,Exception e,ICall<?> callB){
		callB.onError(call, e);
		callB.onAfter();
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private void successCallBack(Object object,ICall finalCallB){
		finalCallB.onResponse(object);
		finalCallB.onAfter();
	}

	/**
	 * @note 同步请求
	 * @author liuh
	 * */
	public String execute(Request request) throws IOException {
		Response response = buildCall(request).execute();
		return response.body().string();
	}

	/**
	 * @note 构造请求call
	 * @author liuh
	 * */
	public Call buildCall(Request request) {
		Call call;
		if (readTimeOut > 0 || writeTimeOut > 0 || connTimeOut > 0) {
			readTimeOut = readTimeOut > 0 ? readTimeOut
					: OkHttpKit.DEFAULT_MILLISECONDS;
			writeTimeOut = writeTimeOut > 0 ? writeTimeOut
					: OkHttpKit.DEFAULT_MILLISECONDS;
			connTimeOut = connTimeOut > 0 ? connTimeOut
					: OkHttpKit.DEFAULT_MILLISECONDS;

			OkHttpClient clone = OkHttpKit.getInstance().getOkHttpClient()
					.newBuilder()
					.readTimeout(readTimeOut, TimeUnit.MILLISECONDS)
					.writeTimeout(writeTimeOut, TimeUnit.MILLISECONDS)
					.connectTimeout(connTimeOut, TimeUnit.MILLISECONDS).build();

			call = clone.newCall(request);
		} else {
			call = OkHttpKit.getInstance().getOkHttpClient().newCall(request);
		}
		return call;
	}

	/**
	 * @note get 请求
	 * @author liuh
	 * */
	public static GetBuilder get() {
		return new GetBuilder();
	}

	/**
	 * @note post 字符串请求
	 * @author liuh
	 * */
	public static PostStringBuilder postString() {
		return new PostStringBuilder();
	}

	/**
	 * @note post 文件请求
	 * @author liuh
	 * */
	public static PostFileBuilder postFile() {
		return new PostFileBuilder();
	}

	/**
	 * @note post PostMultipart
	 * */
	public static PostFormBuilder post() {
		return new PostFormBuilder();
	}

}
